/**
 * @license
 * Copyright 2014 Google Inc. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 *
 * @fileoverview Provides context APIs that all parts (e.g. browser actions,
 * other extension pages, content scripts, etc.) of the extension can use.
 *
 */

goog.provide('e2e.ext.api.Api');

goog.require('e2e.async.Result');
goog.require('e2e.ext.actions.Executor');
goog.require('e2e.ext.api.RequestThrottle');
goog.require('e2e.ext.constants.Actions');
/** @suppress {extraRequire} manually import typedefs due to b/15739810 */
goog.require('e2e.ext.messages.ApiRequest');
goog.require('goog.ui.Component');

goog.scope(function() {
var api = e2e.ext.api;
var constants = e2e.ext.constants;
var messages = e2e.ext.messages;


/**
 * Constructor for the context API.
 */
api.Api = class extends goog.ui.Component {
  constructor() {
    super();

    /**
     * The handler to process incoming requests from other parts of the
     * extension.
     * @type {!function(Port)}
     * @private
     */
    this.requestHandler_ = goog.bind(this.openPort_, this);

    /**
     * A request throttle for incoming decrypt requests.
     * @type {!api.RequestThrottle}
     * @private
     */
    this.requestThrottle_ = new api.RequestThrottle(api.Api.REQUEST_THRESHOLD_);

    /**
     * Executor for the End-to-End actions.
     * @type {!e2e.ext.actions.Executor}
     * @private
     */
    this.actionExecutor_ = new e2e.ext.actions.Executor();
  }

  /** @override */
  disposeInternal() {
    this.removeApi();

    super.disposeInternal();
  }

  /**
   * Installs the API.
   */
  installApi() {
    chrome.runtime.onConnect.addListener(this.requestHandler_);
  }

  /**
   * Removes the API.
   */
  removeApi() {
    chrome.runtime.onConnect.removeListener(this.requestHandler_);
  }

  /**
   * Opens a port with the connecting counterpart.
   * @param {Port} port The port through which communication should carried with
   *     the counterpart.
   * @private
   */
  openPort_(port) {
    port.onMessage.addListener(goog.bind(
        this.executeAction_, this, goog.bind(port.postMessage, port)));
  }

  /**
   * Executes the PGP action and passed the result to the provided callback.
   * @param {function(*)} callback A callback to pass the result of the action
   *     to.
   * @param {*} req The execution request.
   * @private
   */
  executeAction_(callback, req) {
    var incoming = /** @type {!messages.ApiRequest.<string>} */ (req);
    var outgoing = {completedAction: incoming.action};

    // Ensure that only certain actions are exposed via the API.
    switch (incoming.action) {
      case constants.Actions.ENCRYPT_SIGN:
      case constants.Actions.DECRYPT_VERIFY:
        // Propagate the decryptPassphrase if needed.
        incoming.passphraseCallback = function(uid) {
          // Note: The passphrase needs to be known when calling executeAction_.
          return e2e.async.Result.toResult(incoming.decryptPassphrase || '');
        };
        break;
      default:
        outgoing.error = chrome.i18n.getMessage('errorUnsupportedAction');
        callback(outgoing);
        return;
    }

    if (window.launcher && !window.launcher.hasPassphrase()) {
      callback({error: chrome.i18n.getMessage('glassKeyringLockedError')});
      return;
    }

    if (!this.requestThrottle_.canProceed()) {
      outgoing.error = chrome.i18n.getMessage('throttleErrorMsg');
      callback(outgoing);
      return;
    }

    this.actionExecutor_.execute(
        incoming, this,
        function(resp) {
          outgoing.content = resp;
          callback(outgoing);
        },
        function(error) {
          outgoing.error = error.messageId !== undefined ?
              chrome.i18n.getMessage(error.messageId) :
              error.message;
          callback(outgoing);
        });
  }
};


/**
 * The number of allowed requests per minute.
 * @type {number}
 * @private
 * @const
 */
api.Api.REQUEST_THRESHOLD_ = 500;


});  // goog.scope
